//
// stacktrace.cpp
// libprotea
// Tools Library for the Protea Project / Functionality for managing stacktraces
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

#include "defines.h"
#include "stacktrace.h"
#include "Fields/fields.h"
#include "AppHandler/AppInterface.h"
#include "OSDep/dir.h"
#include "OSDep/files.h"
#include "OSDep/process.h"
#include "defns.h"
using namespace ProteaApp1;

#include <stdarg.h>

// Timer frequency
STACKTRACE_TIME_TYPE perf_freq;

using namespace std;

bool InErrorState = false;
char errloc[ MAX_PATH ];


// Callstack structure
struct callstack_info {
	const char* func_name;
	int line;
	const char* file;
};

typedef vector<callstack_info*> call_stack_vector;

XCTLS CallStack;
XCCriticalSection PerfCounters;

void XCAddFunction( const char *func_name, const int line, const char* file ) {
	callstack_info* temp = new callstack_info;
	temp->func_name = func_name;
	temp->line = line;
	temp->file = file;

	call_stack_vector* stack = (call_stack_vector*)CallStack.GetData();
	if (!stack) {
		stack = new call_stack_vector;
		CallStack.SetData( stack );
	};
	stack->push_back( temp );
};



void XCClearCallStack() {
	call_stack_vector* stack = (call_stack_vector*)CallStack.GetData();
	if (stack) {
		for (unsigned int i=0; i<stack->size(); i++)
			delete (callstack_info*)(*stack)[i];
		stack->clear();

		delete stack;
		CallStack.SetData( NULL );
	};
};



void XCOutputCallStack( XCAppInterface* App, int err_num, char* err_descr ) {
	char temp[ 1024 ];

	cerr << endl << "*** APPLICATION ERROR ***" << endl;
	const char* process_name = NULL;
	if (App) {
		process_name = App->GetProcessName();
		if (process_name)
			cerr << process_name << endl;
	};

	if (err_descr)
		cerr << err_descr << endl;
	else
		cerr << "Error Number: " << err_num << endl;
	cerr << "*************************" << endl;

	if (err_num != ERR_INV_LICENSE) {
		call_stack_vector* stack = (call_stack_vector*)CallStack.GetData();
		if (stack) {
			int size = stack->size() - 1;

/*
			if (App) {
				App->ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "--------" );
				if (err_descr)
					App->ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, err_descr );
				else
					App->ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "%d", err_num );
				App->ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "--------" );
				App->ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Current call stack: " );
			} else {
*/
			{
				cerr << "Current call stack: " << endl;
			};

			for (int i = size; i>=0; i--) {
				const callstack_info* current = (*stack)[i];

				if (current->line > 0)
					sprintf( temp, "  %s (%d,%s)", current->func_name, current->line, current->file );
				else
					sprintf( temp, "  %s (%s)", current->func_name, current->file );

/*
				if (App)
					App->ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, temp );
				else
*/
					cerr << temp << endl;

				delete (callstack_info*)(*stack)[i];
			};

			stack->clear();
		} else
			cerr << "No stack available" << endl;
		cerr << "*************************" << endl;
	};
};


//
// Subroutine entrance/exit tracking
//
//	Also contains profiling code to track execution times
//
void XCEnterSubroutine( XCAppInterface* TOOLSAPP, const char* funcname ) {
	if (TOOLSAPP) {
		TOOLSAPP->ToolsTrace( TRC_FUNCTIONS, TLV_HIGH, "(%d) IN : %s", ++TOOLSAPP->func_level, funcname ); 

		// Allocate a new level in the performance counters
		tPerfCounters* temp = new tPerfCounters;

		STACKTRACE_ENTER;

		PerfCounters.Lock();
		temp->next = TOOLSAPP->perf_counters;
		TOOLSAPP->perf_counters = temp;
		PerfCounters.UnLock();
	};
};


void XCLeaveSubroutine( XCAppInterface* TOOLSAPP, const char* funcname, const long linenum, long result ) {

	if (TOOLSAPP) {
		long timing = 0;

		PerfCounters.Lock();

		tPerfCounters* temp = TOOLSAPP->perf_counters;
		if (temp) {

			STACKTRACE_LEAVE;

			// Deallocate last performance counter
			tPerfCounters* killme = temp;
			TOOLSAPP->perf_counters = temp->next;
			delete killme;
		};

		PerfCounters.UnLock();

		TOOLSAPP->ToolsTrace( TRC_FUNCTIONS, TLV_HIGH, "(%d) OUT: %-31.31s Line=%ld (result=%10d) [ET=%ld]", TOOLSAPP->func_level--, funcname, linenum, (long)result, timing );
	};
};








double XCTiming::Stop() {
	last_result = GetCurrent();
	return last_result;
};
